Pagination: Error While Converting Parameter Value into Integer - haskell

I know there is a Paginator package for Yesod but I prefer a simpler UI so I was creating a simple pagination logic for my app. However, I couldn't figure out a way to convert the parameter value to Integer.
import Data.Text (unpack, singleton)
import Data.Maybe
one = singleton '1' -- convert char to Text, required by fromMaybe
getTestPanelR :: Handler Html
getTestPanelR = do
ptext <- lookupGetParam "p" -- guessing returns Maybe Text
p <- fromMaybe one ptext -- ??? does not work
-- pn <- ??? Once p is extracted successfully, how to convert to an integer?
s <- runDB $ selectList [] [Asc PersonName, LimitTo 10 , OffsetBy $ (pn - 1) * 10]
(widget, enctype) <- generateFormPost $ entryForm Nothing
defaultLayout $ do
$(widgetFile "person")
When I run the above Code I get the following error message:
No instance for (MonadHandler Maybe)
arising from a use of `lookupGetParam'
Possible fix: add an instance declaration for (MonadHandler Maybe)
In the second argument of `($)', namely `lookupGetParam "p"'
In a stmt of a 'do' block:
p <- fromMaybe one $ lookupGetParam "p"
In the expression:
...
When I write out 'ptext' using #{show ptext} it shows Just "1". Having gotten the GET parameter, how do I convert it to an integer so I can do pagination? (need to add 1 for 'next' and subtract 1 for 'prev')
FWIW, when I try this using GHCi, it works fine:
Prelude Data.Maybe Data.Text> let one = singleton '1'
Prelude Data.Maybe Data.Text> let x = Just $ singleton '5'
Prelude Data.Maybe Data.Text> let y = fromMaybe one x
Prelude Data.Maybe Data.Text> y
"5"
Prelude Data.Maybe Data.Text> read $ Data.Text.unpack y ::Int -- This is probably unsafe because I cannot trust 'y' in my web app
5
Update:
I tired #Ankur's suggestion pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . fromMaybe "1") and I get the following error:
Couldn't match expected type `String' with actual type `Text'
Expected type: Maybe Text -> String
Actual type: Maybe Text -> Text
In the return type of a call of `fromMaybe'
In the second argument of `(.)', namely `fromMaybe "1"'
Build failure, pausing...
If change the "1" to one (Data.Text.singleton '1'), I still get the exact same error message.
Thanks!

lookupGetParam returns ParamValue which is type ParamValue = String. So basically it is String rather than Text.
Try this:
pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . fromMaybe "1")
UPDATE:
Actually the latest version of lookupGetParam is Text based so adding the OverloadedStrings language extension should get the job done:
Put this {-# LANGUAGE OverloadedStrings #-} at the start of the code file and use:
pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . unpack . fromMaybe "1")

Related

read on int works in isolation, but how to get it to work in this script?

When I try to call read to read an Int32, it works in GHCi:
$ ghci
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> import Data.Int
Prelude Data.Int> let fooInt32 :: Int32; fooInt32 = read "6531"
Prelude Data.Int> :t fooInt32
fooInt32 :: Int32
Prelude Data.Int> fooInt32
6531
However, in my app, I get this error (used a wrapped read for better error messages):
readInt32.hs: read error, reading: '6531'
CallStack (from HasCallStack):
error, called at /home/brandon/workspace/ProjectGists/Haskell/ReadInt32/readInt32.hs:27:9 in main:Main
Here's a self-contained stack script that exemplifies the error:
#!/usr/bin/env stack
-- stack --resolver lts-13.14 script
{-# LANGUAGE OverloadedStrings #-}
import Data.Int
import Data.Maybe (fromJust, isJust, listToMaybe)
import Data.String (IsString(..))
import Data.Text (Text, pack, splitOn, unpack)
import Data.Time.Calendar (Day(..))
import Data.Time.Clock (UTCTime(..))
import Data.Typeable
newtype CowMark = CowMark {unCowMark :: Int32}
deriving (Eq, Ord, Read, Show, Typeable)
newtype TableName = TableName {unTableName :: Text}
deriving (Eq, Ord, Read, Show)
instance IsString TableName where
fromString str = TableName $ pack str
type CowRecordKey = (TableName, CowMark, UTCTime)
read' :: Read a => String -> a
read' s = case reads s of
[(x,"")] -> x
_ -> error $ " read error, reading: '" ++ s ++ "'"
rep2Key :: String -> Maybe CowRecordKey
rep2Key strKey = do
splList <- return $ splitOn "_" (pack strKey)
(tblName, mrkStr, timeStr) <- head3 splList
miInt <- read' (unpack mrkStr)
timeOut <- read (unpack timeStr)
return $ (TableName $ tblName, CowMark miInt, timeOut)
where
head3 :: [a] -> Maybe (a, a, a)
head3 list#(_:x2:xs) = do
mx1 <- listToMaybe list
mx2 <- listToMaybe (x2:xs)
mx3 <- listToMaybe xs
return (mx1, mx2, mx3)
head3 _ = Nothing
cowRecKey1 :: CowRecordKey
cowRecKey1 = (
TableName "SxRecord"
, CowMark 6531
, UTCTime (ModifiedJulianDay 3234) 0
)
cowRecKeyStr1 :: String
cowRecKeyStr1 = "SxRecord_6531_1867-09-25 00:00:00"
main = do
key1 <- return $ rep2Key cowRecKeyStr1
print key1
Not really sure what to make of this. I've also tried annotating miInt :: Int32 to see if that helped, but it didn't.
I've annotated read' in your script with the type expected from it:
read' :: String -> Int32
read' s = case reads s of
[(x,"")] -> x
_ -> error $ " read error, reading: '" ++ s ++ "'"
and immediately got an error:
• Couldn't match expected type ‘Maybe Int32’
with actual type ‘Int32’
• In a stmt of a 'do' block: miInt <- read' (unpack mrkStr)
Indeed, the do block in rep2Key is run in the Maybe context, so the line
miInt <- read' (unpack mrkStr)
binds miInt :: Int32, but the expression on the right has the type Maybe Int32. This is why annotating miInt by itself didn't point to the problem.
If you want to fail the parsing (?), change read' to return a Maybe, like this:
read' :: Read a => String -> Maybe a
read' s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
If you want to keep the error behavior (and read' signature), or for any other functions that return a pure value, you can change the line in rep2Key to
let miInt = read' (unpack mrkStr)
Here miInt is bound to the value from the right hand side without invoking >>=, so it'll be expected to be the same type (Int32 without Maybe).
A Hoogle search shows that this last function exists in Prelude as Text.Read.readMaybe.

Error handling in pipes

Backstory
I have a number of data files, each of them containing a list of data records (one per line).
Similar to CSV but sufficiently different that I'd prefer to write my own parser rather than using a CSV library.
For the purpose of this question I will use a simplified data file that contains just one number per line:
1
2
3
error
4
As you can see it is possible that a file contains malformed data in which case the whole file should be considered malformed.
The kind of data-processing I want to do can be expressed in terms of maps and folds.
So, I thought this would be a good opportunity to learn how to use the pipes library.
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Except
import Pipes ((>->))
import qualified Pipes as P
import qualified Pipes.Prelude as P
import qualified Pipes.Safe as P
import qualified System.IO as IO
First, I create a producer of lines in the text file.
This is very similar to the example in the docs of Pipes.Safe.
getLines = do
P.bracket (IO.openFile "data.txt" IO.ReadMode) IO.hClose P.fromHandle
Next, I need a function to parse each of these lines.
As I mentioned before, this might fail, which I will represent with Either.
type ErrMsg = String
parseNumber :: String -> Either ErrMsg Integer
parseNumber s = case reads s of
[(n, "")] -> Right n
_ -> Left $ "Parse Error: \"" ++ s ++ "\""
For simplicity, as a first step, I want to collect all data records into a list of records.
The most straight-forward approach is to pipe all the lines through the parser and just collect the whole thing into a list.
readNumbers1 :: IO [Either ErrMsg Integer]
readNumbers1 = P.runSafeT $ P.toListM $
getLines >-> P.map parseNumber
Unfortunately, that creates a list of eithers of records.
However, if the file contains one wrong record then the whole file should be considered wrong.
What I really want is an either of a list of records.
Of course I can just use sequence to transpose the list of eithers.
readNumbers2 :: IO (Either ErrMsg [Integer])
readNumbers2 = sequence <$> readNumbers1
But, that would read the whole file even if the first line is already malformed.
These files can be large and I have many of them, so, it would be better if the reading would stop at the first error.
Question
My Question is how to achieve that.
How to abort parsing upon the first malformed record?
What I got so far
My first thought was to use the monad instance of Either ErrMsg and P.mapM instead of P.map.
Since we are reading from a file we already have IO and SafeT in our monad stack, so, I guess I'll need ExceptT to get error handling into that monad stack.
This is the point where I'm stuck.
I tried many different combinations and always ended up being yelled at by the type-checker.
The following is the closest I can get to it compiles.
readNumbers3 = P.runSafeT $ runExceptT $ P.toListM $
getLines >-> P.mapM (ExceptT . return . parseNumber)
The infered type of readNumbers3 reads
*Main> :t readNumbers3
readNumbers3
:: (MonadIO m, P.MonadSafe (ExceptT ErrMsg (P.SafeT m)),
P.MonadMask m, P.Base (ExceptT ErrMsg (P.SafeT m)) ~ IO) =>
m (Either ErrMsg [Integer])
which looks close to what I want:
readNumbers3 :: IO (Either ErrMsg [Integer])
However, as soon as I try to actually execute that action I get the following error message in ghci:
*Main> readNumbers3
<interactive>:7:1:
Couldn't match expected type ‘IO’
with actual type ‘P.Base (ExceptT ErrMsg (P.SafeT m0))’
The type variable ‘m0’ is ambiguous
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it
If I try to apply the following type-signature:
readNumbers3 :: IO (Either ErrMsg [Integer])
Then I get the following error message:
error.hs:108:5:
Couldn't match expected type ‘IO’
with actual type ‘P.Base (ExceptT ErrMsg (P.SafeT IO))’
In the first argument of ‘(>->)’, namely ‘getLines’
In the second argument of ‘($)’, namely
‘getLines >-> P.mapM (ExceptT . return . parseNumber)’
In the second argument of ‘($)’, namely
‘P.toListM $ getLines >-> P.mapM (ExceptT . return . parseNumber)’
Failed, modules loaded: none.
Aside
Another motivation for moving the error handling into the pipe's base monad is that it would make further data processing much easier if I wouldn't have to juggle with eithers in my maps and folds.
Here is an incremental approach to solving the problem.
Following Tekmo's suggestion in this SO answer
we aim to operate in the following monad:
ExceptT String (Pipe a b m) r
We begin with imports and the definition of parseNumber:
import Control.Monad.Except
import Pipes ((>->))
import qualified Pipes as P
import qualified Pipes.Prelude as P
parseNumber :: String -> Either String Integer
parseNumber s = case reads s of
[(n, "")] -> Right n
_ -> Left $ "Parse Error: \"" ++ s ++ "\""
Here is a plain Producer of Strings in the IO-monad we'll use as our input:
p1 :: P.Producer String IO ()
p1 = P.stdinLn >-> P.takeWhile (/= "quit")
To lift it to the ExceptT monad we just use lift:
p2 :: ExceptT String (P.Producer String IO) ()
p2 = lift p1
Here is a pipeline segment which converts Strings to Integers in the ExceptT monad:
p4 :: ExceptT String (P.Pipe String Integer IO) a
p4 = forever $
do s <- lift P.await
case parseNumber s of
Left e -> throwError e
Right n -> lift $ P.yield n
The probably can be written more combinatorially, but I've left it very explicit for clarity.
Next we join p2 and p4 together. The result is also in the ExceptT monad.
-- join together p2 and p4
p7 :: ExceptT String (P.Producer Integer IO) ()
p7 = ExceptT $ runExceptT p2 >-> runExceptT p4
Tekmo's SO answer suggests creating a new operator for this.
Finally, we can use toListM' to run this pipeline. (I've included the definition of toListM' here because it doesn't appear in my installed version of Pipes.Prelude)
p8 :: IO ([Integer], Either String ())
p8 = toListM' $ runExceptT p7
toListM' :: Monad m => P.Producer a m r -> m ([a], r)
toListM' = P.fold' step begin done
where
step x a = x . (a:)
begin = id
done x = x []
Examples of how p8 works:
ghci> p8
4
5
6
quit
([4,5,6],Right ())
ghci> p8
5
asd
([5],Left "Parse Error: \"asd\"")
Update
You can simplify the code by generalizing parseNumber like this:
parseNumber' :: (MonadError [Char] m) => String -> m Integer
parseNumber' s = case reads s of
[(n, "")] -> return n
_ -> throwError $ "Parse Error: \"" ++ s ++ "\""
Then p4 may be written:
p4' :: ExceptT String (P.Pipe String Integer IO) a
p4' = forever $ lift P.await >>= parseNumber' >>= lift . P.yield

Find all text inputs on webpage with Haskell webdriver package

Here is my attempt at finding all inputs of type "text" on a webpage. I have since figured out I could use xpath, but I'd like to know how to make the way I attempted work. I'm most interested in how I would lifft my [Element] into the [WD Element] and make this program valid.
However if my approach is just wrong or unidiomatic, feel free to totally rewrite it. Here is the code:
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import Control.Monad.IO.Class
import Test.WebDriver
import Test.WebDriver.Classes (WebDriver (..))
import Test.WebDriver.Commands
import Test.WebDriver.Commands.Wait
main = do
runSession defaultSession capabilities $ do
openPage "http://www.appnitro.com/demo/view.php?id=1"
inputs <- findElems $ ByTag "input"
textElems <- filterM (liftM $ ((==) "text" . (`attr` "type"))) inputs
-- wait 20 seconds
waitUntil 20 (getText <=< findElem $ ByCSS ".doesnotexist")
`onTimeout` return ""
liftIO $ putStrLn "done"
where
capabilities = allCaps { browser=firefox }
-- [1 of 1] Compiling Main ( src/Main.hs, interpreted )
-- src/Main.hs:168:70:
-- Couldn't match type `Element' with `WD Element'
-- Expected type: [WD Element]
-- Actual type: [Element]
-- In the second argument of `filterM', namely `inputs'
-- In a stmt of a 'do' block:
-- textElems <- filterM
-- (liftM $ ((==) "text" . (`attr` "type"))) inputs
-- In the second argument of `($)', namely
-- `do { openPage "http://www.appnitro.com/demo/view.php?id=1";
-- inputs <- findElems $ ByTag "input";
-- textElems <- filterM
-- (liftM $ ((==) "text" . (`attr` "type"))) inputs;
-- waitUntil 20 (getText <=< findElem $ ByCSS ".doesnotexist")
-- `onTimeout` return "" }'
-- Failed, modules loaded: none.
This might not be the answer you were looking for, but I find it best to express these kinds of constraints on elements directly in the locators, in order not to clutter the haskell sources. As you mention using XPath is one possibility:
textElems <- findElems $ ByXPath "//input[#type='text']"
but I often prefer CSS selectors, which tend to be briefer (alas a bit less powerful - e.g. you cannot traverse from given element to its parent etc.) and work just as well in these simple cases
textElems <- findElems $ ByCSS "input[type='text']"
Replace your text elem filtering with this piece:
textElems <- filterM textElem inputs
And then add this where:
textElem e = (== Just "text") `fmap` (e `attr` "type")
But then you will still have the line with waitUntil that doesn't compile. That seems unrelated though as it has nothing to do with the filtering.
You have two errors in your code:
attr returns a Maybe value, thus you must compare with Just "text".
And given that attr already returns a value in the WD monad, you just have to lift just the first function (has you had it, you were lifting everything, including the attr function):
textElems <- filterM (liftM ((==) (Just "text")) . (`attr` "type")) inputs
Or alternatively, since WD is also a Functor (I actually find this easier to understand, you are applying a pure function inside the monad returned by attr):
textElems <- filterM (fmap ((==) (Just "text")) . (`attr` "type")) inputs

Function out of scope

I have a doubt of why is this happening. I have been following the "Yesod Web" ebook but with a scaffolded site. When I arrived to a position that I wanted to apply the function of "plural" inside a "messages" file, the compiler returns this error:
Foundation.hs:52:1: Not in scope: `plural'
Where plural is declared in the same hs file as the one that I am calling the "hamlet" one. However, if I move the function declaration before the line #52 in the "Foundation.hs" file, then the error vanishes and it let me compile it effectively. Why does this happen??
module Handler.UserProfile where
import Import
import Data.Maybe (fromMaybe)
import Data.Text (pack, unpack)
viewCountName :: Text
viewCountName = "UserProfileViews"
readInt :: String -> Int
readInt = read
plural :: Int -> String -> String -> String
plural 1 x _ = x
plural _ _ y = y
getUserProfileR :: Handler RepHtml
getUserProfileR = do
viewCount <- lookupSession viewCountName
>>= return . (1 +) . readInt . unpack . fromMaybe "0"
setSession viewCountName (pack $ show viewCount)
maid <- maybeAuth
--msg <- getMessageRender
let user = case maid of
Nothing -> "(Unknown User ID)" --show MsgHello --
Just (Entity _ u) -> userEmail u
defaultLayout $ do
setTitleI MsgUserProfile
$(widgetFile "nhUserProfile")
Take a look at the GHC users manual: http://www.haskell.org/ghc/docs/7.4.2/html/users_guide/template-haskell.html#id684916
The staging restriction in play is described in the second bullet point:
You can only run a function at compile time if it is imported from
another module. That is, you can't define a function in a module, and
call it from within a splice in the same module. (It would make sense
to do so, but it's hard to implement.)

How to use MultiPiece

I'm completely new to Yesod (and not very experienced in haskell) and I'm trying to build my first handler. I scraffolded my app using default parameters (I'm using Yesod 0.9.4.1 version and choose postgresql in scraffolding) and now I'm trying to retrieve some data from a table using selectList. I defined a new table (let's call it Foo) in models config file:
Foo
xStart Int
yStart Int
and want to pass a list of FooId's and some other Foo attributes so I defined a route:
/foos/#Int/#Int/*FooId FoosReturnR GET
and a handler:
module Handler.FoosReturn where
import Import
selectWindowSize :: Int
selectWindowSize = 10000
getFoosReturnR :: Int -> Int -> [FooId] -> Handler RepPlain
getFoosReturnR x y withoutIds = do
foos <- runDB $ selectList [FooId /<-. withoutIds,
FooXStart <. x + selectWindowSize,
FooXStart >=. x - selectWindowSize,
FooYStart <. y + selectWindowSize,
FooYStart >=. y - selectWindowSize] []
return $ RepPlain $ toContent $ show foos
I imported the handler in Application.hs and added it to cabal file and now when I'm trying to run it I receive an error saying that FooId is not an instance of MultiPiece - but when I try to make it an instance there is an error saying that FooId is a type synonym and cannot be an instance of MultiPiece - how to resolve this problem?
EDIT:
Daniel: well, actually I don't know what exactly is FooId - it's a part of Yesod's magic which I don't fully understand so far - it's generated automatically from the table definition - but it's a some kind of a number.
Because I don't know how to use MultiPiece I switched to simpler solution and modified:
route: /foos/#Int/#Int/#String FoosReturnR GET
handler: [added also some logging]
module Handler.FoosReturn where
import Import
import Data.List.Split
import qualified Data.Text.Lazy as TL
selectWindowSize :: Int
selectWindowSize = 10000
getFoosReturnR :: Int -> Int -> String -> Handler RepPlain
getFoosReturnR x y withoutIds = do
app <- getYesod
liftIO $ logLazyText (getLogger app) ("getFoosReturnR('" `TL.append` (TL.pack $ (show x) ++ "', '" ++ (show y) ++ "', '" ++ withoutIds ++ "') "))
foos <- runDB $ selectList [FooId /<-. (map (\a -> read a :: FooId) $ splitOn "," withoutIds),
FooXStart <. x + selectWindowSize,
FooXStart >=. x - selectWindowSize,
FooYStart <. y + selectWindowSize,
FooYStart >=. y - selectWindowSize] []
return $ RepPlain $ toContent $ show foos
and now it is compiling but when I browse to: http://localhost:3000/sectors/1/1/1,2 I get a page containing only:
Internal Server Error
Prelude.read: no parse
Well, I don't fully understand what is FooId here - how to create such a list of FooId's from list of strings containing numbers?
And of course a solution of how to make the FooId an instance of MultiPiece is most wanted.
EDIT:
Daniel and svachalek, thanks for your posts - I tried your (Daniel's) solution but then I was receiving errors saying that [FooId] is expected (as in the handler function declaration) but FooId type was given and this lead me to the following solution:
data FooIds = FooIds [FooId] deriving (Show, Read, Eq)
instance MultiPiece FooIds where
toMultiPiece (FooIds fooList) = map (Data.Text.pack . show) fooList
fromMultiPiece texts =
if length (filter isNothing listOfMaybeFooId) > 0
then Nothing
else Just $ FooIds $ map fromJust listOfMaybeFooId
where
listOfMaybeFooId = map constructMaybeFooId texts
constructMaybeFooId :: Text -> Maybe FooId
constructMaybeFooId x = case reads (Data.Text.unpack x) :: [(FooId,String)] of
[(foo,_)] -> Just foo
_ -> Nothing
of course I changed the route to: /foos/#Int/#Int/*FooIds FoosReturnR GET
and the handler to:
getFoosReturnR :: Int -> Int -> FooIds -> Handler RepPlain
getFoosReturnR coordX coordY (FooIds withoutIds) = do
and now I don't get any errors during compilation nor runtime, and the only not satisfying thing is that I always receive Not Found as a result, even if I supply parameters that should give me some results - so now I have to figure out how to determine what SQL was exactly sent to the database
EDIT:
Now I see that the "Not Found" is connected to the problem and that the above edit is not a solution - when I browse to localhost:3000/foos/4930000/3360000 then I get the results (but then the FooIds is empty) - but when I add something like: localhost:3000/sectors/4930000/3360000/1 then I always get "Not Found" - so it's still not working..
Wish I could help, but yesod has something to do with web applications, as far as I know, hence I've never really looked at it. So I can just try a stab in the air, maybe I hit something.
Hayoo leads to
class MultiPiece s where
fromMultiPiece :: [Text] -> Maybe s
toMultiPiece :: s -> [Text]
in Yesod.Dispatch. Since FooId seems to have a Read instance and probably a Show instance, you could try
{-# LANGUAGE TypeSynonymInstances #-}
-- maybe also FlexibleInstances
instance MultiPiece FooId where
toMultiPiece foo = [Text.pack $ show foo]
fromMultiPiece texts =
case reads (unpack $ Text.concat texts) :: [(FooId,String)] of
[(foo,_)] -> Just foo
_ -> Nothing
I have no idea whether that is close to the right thing, and I would have posted it as a comment, but it's too long and there's not much formatting in comments. If it doesn't help I will delete it to not give the impression your question already has an answer when it hasn't.
The problem is solved:)
You could either use my implementation from one of the last edits of the question and browse to URL like: http://localhost:3000/foos/4930000/3360000/Key {unKey = PersistInt64 3}/Key {unKey = PersistInt64 4}
The Key type derives Read but not in a very friendly (and expected) way:)
Or change the implementation of fromMultiPiece to:
instance MultiPiece FooIds where
toMultiPiece (FooIds fooList) = map (Data.Text.pack . show) fooList
fromMultiPiece texts =
if length (filter isNothing listOfMaybeFooId) > 0
then Nothing
else Just $ FooIds $ map fromJust listOfMaybeFooId
where
listOfMaybeFooId = map constructMaybeFooId texts
constructMaybeFooId :: Text -> Maybe FooId
constructMaybeFooId x = case TR.decimal x of
Left err -> Nothing
Right (el,_) -> Just $ Key (PersistInt64 el)
and use URLs like: http://localhost:3000/foos/4930000/3360000/1/2
Many thanks to David McBride from the Yesod Web Framework Google Group
EDIT: the above solution had only one disadvantage - using the PersistInt64 type - it's not a good practice to use such a details of implementation, but it can be repaired by using fromPersistValue and toPersistValue functions from Database.Persist as follows:
instance MultiPiece FooIds where
toMultiPiece (FooIds fooList) = map (persistValuetoText . unKey) fooList
where
persistValuetoText x = case fromPersistValue x of
Left _ -> Data.Text.pack ""
Right val -> Data.Text.pack $ show (val::Int)
fromMultiPiece texts =
if length (filter isNothing listOfMaybeFooId) > 0
then Nothing
else Just $ FooIds $ map fromJust listOfMaybeFooId
where
listOfMaybeFooId = map constructMaybeFooId texts
constructMaybeFooId :: Text -> Maybe FooId
constructMaybeFooId x = case TR.decimal x of
Left _ -> Nothing
Right (el,_) -> Just $ Key (toPersistValue (el :: Int))
Again, big thanks to David McBride also for this!
I'm also fairly new to Yesod and I gave in and added -XTypeSynonymInstances to the ghc-options in my .cabal file, and so far it's made life a lot easier for me. I'm not sure if it's the most elegant answer to this particular problem, but otherwise I predict you'll run into that instance-of-alias error pretty frequently. P.S. try id = (Key (PersistInt 64 n))

Resources