What other prelude to cleanly convert text-files into json

Dear reader, I wrote some tiny code to parse files into json (to automate escaping multi-line text). It quickly became a collection of "import qualified" and "T.unpack, T.pack" calls that made the whole thing look ugly [1]. What "alternative prelude" would help in getting clean code when combining "aeson" and reading text files? Greetings, Bram [1] Source can be read here: https://gist.github.com/bneijt/9bdb4b1759790a8463c9

I haven't looked at your code since I'm on a mobile, but classy-prelude, of
which I'm one of the maintainers, does provide functions that generally
work with the Text data type.
On Sat, Feb 28, 2015, 1:24 PM Bram Neijt
Dear reader,
I wrote some tiny code to parse files into json (to automate escaping multi-line text).
It quickly became a collection of "import qualified" and "T.unpack, T.pack" calls that made the whole thing look ugly [1].
What "alternative prelude" would help in getting clean code when combining "aeson" and reading text files?
Greetings,
Bram
[1] Source can be read here: https://gist.github.com/ bneijt/9bdb4b1759790a8463c9 _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 2015年02月28日 20:23, Bram Neijt wrote:
It quickly became a collection of "import qualified" and "T.unpack, T.pack" calls that made the whole thing look ugly [1]. <SNIP> [1] Source can be read here: https://gist.github.com/bneijt/9bdb4b1759790a8463c9
File paths are of type `System.FilePath.Posix.FilePath`, a type alias for `String`. Note that this convention is also followed in `ByteString` and `Text` libraries; they do not use `ByteString` or `Text` types for the file paths. In this code, file paths are packed only to have to unpack them again (twice!), which likely offsets any performance improvements of using the `Text` version of `isSuffixOf`. Here is a version using the same style but without packing file paths: https://gist.github.com/TravisCardwell/fd9981e4968e4af3751d I included a few other changes, which are commented in the code. By the way, I do not think that qualified imports are bad. I like them because they provide an explicit reference to the source module of a function. Cheers, Travis

Thank you all for the responses.
Qualified imports for a script of less then a page make the code less
readable. That is why I want to specifically import subsets of
functions and not prefix everything with qualified imports.
I've taken the code from Travis (clean up some of the useless
pack/unpack), decided to use "NoImplicitPrelude" and then I had this:
#!/usr/bin/runghc
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
import Prelude (Show, IO, (++), print, String, filter)
import Data.List (isSuffixOf)
import Data.Text hiding (isSuffixOf, filter)
import Data.Text.IO
import Control.Monad (mapM_)
import qualified Data.ByteString.Lazy as BL
import System.Directory (getDirectoryContents)
import System.FilePath.Posix ((<.>), FilePath, takeBaseName)
import Data.Aeson
data Product = Product
{ image :: Text
, description :: Text
} deriving Show
instance ToJSON Product where
toJSON (Product image description) = object ["image" .= image,
"description" .= description]
encodeToJson :: FilePath -> IO()
encodeToJson srcName = do
let jsonName = takeBaseName srcName <.> "json"
contents <- readFile srcName
let contentLines = lines contents
case contentLines of -- head is unsafe! try your code on an empty file
(firstLine : restLines) -> BL.writeFile jsonName (encode Product {
image = firstLine,
description = unlines restLines
})
_ -> print ("error: invalid source file: " ++ srcName)
main = do
names <- getDirectoryContents "."
let srcNames = filter (isSuffixOf ".src") names
mapM_ encodeToJson srcNames
What bugs me is that this is a really simple thing to do in any other
language, but here I seem to be stuck between Prelude and Text and as
a beginner Prelude is king.
I tried ClassyPrelude but got stuck at not being able to get the
result of getDirectoryContents into the ClassyPrelude version of
mapM_. Sorry but I gave up on that.
I think that given all this, the above code is as beautiful as Haskell
can pull this off at the moment.
If somebody can prove me wrong, I would love to still see it.
Greetings,
Bram
On Sun, Mar 1, 2015 at 1:42 AM, Travis Cardwell
On 2015年02月28日 20:23, Bram Neijt wrote:
It quickly became a collection of "import qualified" and "T.unpack, T.pack" calls that made the whole thing look ugly [1]. <SNIP> [1] Source can be read here: https://gist.github.com/bneijt/9bdb4b1759790a8463c9
File paths are of type `System.FilePath.Posix.FilePath`, a type alias for `String`. Note that this convention is also followed in `ByteString` and `Text` libraries; they do not use `ByteString` or `Text` types for the file paths.
In this code, file paths are packed only to have to unpack them again (twice!), which likely offsets any performance improvements of using the `Text` version of `isSuffixOf`.
Here is a version using the same style but without packing file paths: https://gist.github.com/TravisCardwell/fd9981e4968e4af3751d
I included a few other changes, which are commented in the code.
By the way, I do not think that qualified imports are bad. I like them because they provide an explicit reference to the source module of a function.
Cheers,
Travis _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

The trick with ClassyPrelude is to use the system-fileio package instead of
straight directory. With that in place, you get:
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
import ClassyPrelude
import Data.Aeson
import Filesystem (listDirectory)
data Product = Product
{ image :: Text
, description :: Text
} deriving Show
instance ToJSON Product where
toJSON (Product image description) = object ["image" .= image,
"description" .= description]
encodeToJson :: FilePath -> IO()
encodeToJson srcName = do
let jsonName = basename srcName <.> "json"
contents <- readFile srcName
let contentLines = lines contents
case contentLines of -- head is unsafe! try your code on an empty file
(firstLine : restLines) -> writeFile jsonName (encode Product {
image = firstLine,
description = unlines restLines
})
_ -> print ("error: invalid source file: " ++ srcName)
main = do
names <- listDirectory "."
let srcNames = filter (flip hasExtension "src") names
mapM_ encodeToJson srcNames
On Sun, Mar 1, 2015 at 6:47 PM Bram Neijt
Thank you all for the responses.
Qualified imports for a script of less then a page make the code less readable. That is why I want to specifically import subsets of functions and not prefix everything with qualified imports.
I've taken the code from Travis (clean up some of the useless pack/unpack), decided to use "NoImplicitPrelude" and then I had this:
#!/usr/bin/runghc {-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-} import Prelude (Show, IO, (++), print, String, filter) import Data.List (isSuffixOf) import Data.Text hiding (isSuffixOf, filter) import Data.Text.IO import Control.Monad (mapM_)
import qualified Data.ByteString.Lazy as BL import System.Directory (getDirectoryContents) import System.FilePath.Posix ((<.>), FilePath, takeBaseName)
import Data.Aeson
data Product = Product { image :: Text , description :: Text } deriving Show
instance ToJSON Product where toJSON (Product image description) = object ["image" .= image, "description" .= description]
encodeToJson :: FilePath -> IO() encodeToJson srcName = do let jsonName = takeBaseName srcName <.> "json" contents <- readFile srcName let contentLines = lines contents case contentLines of -- head is unsafe! try your code on an empty file (firstLine : restLines) -> BL.writeFile jsonName (encode Product { image = firstLine, description = unlines restLines }) _ -> print ("error: invalid source file: " ++ srcName)
main = do names <- getDirectoryContents "." let srcNames = filter (isSuffixOf ".src") names mapM_ encodeToJson srcNames
What bugs me is that this is a really simple thing to do in any other language, but here I seem to be stuck between Prelude and Text and as a beginner Prelude is king.
I tried ClassyPrelude but got stuck at not being able to get the result of getDirectoryContents into the ClassyPrelude version of mapM_. Sorry but I gave up on that.
I think that given all this, the above code is as beautiful as Haskell can pull this off at the moment.
If somebody can prove me wrong, I would love to still see it.
Greetings,
Bram
On Sun, Mar 1, 2015 at 1:42 AM, Travis Cardwell
wrote: On 2015年02月28日 20:23, Bram Neijt wrote:
It quickly became a collection of "import qualified" and "T.unpack, T.pack" calls that made the whole thing look ugly [1]. <SNIP> [1] Source can be read here: https://gist.github.com/ bneijt/9bdb4b1759790a8463c9
File paths are of type `System.FilePath.Posix.FilePath`, a type alias for `String`. Note that this convention is also followed in `ByteString` and `Text` libraries; they do not use `ByteString` or `Text` types for the file paths.
In this code, file paths are packed only to have to unpack them again (twice!), which likely offsets any performance improvements of using the `Text` version of `isSuffixOf`.
Here is a version using the same style but without packing file paths: https://gist.github.com/TravisCardwell/fd9981e4968e4af3751d
I included a few other changes, which are commented in the code.
By the way, I do not think that qualified imports are bad. I like them because they provide an explicit reference to the source module of a function.
Cheers,
Travis _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Thank you!
I think that looks much better then anything I got working.
Using another Prelude looks the best in this case, but without your
help I would have never found system-fileio.
Bram
On Sun, Mar 1, 2015 at 5:52 PM, Michael Snoyman
The trick with ClassyPrelude is to use the system-fileio package instead of straight directory. With that in place, you get:
{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-} import ClassyPrelude import Data.Aeson import Filesystem (listDirectory)
data Product = Product { image :: Text , description :: Text } deriving Show
instance ToJSON Product where toJSON (Product image description) = object ["image" .= image, "description" .= description]
encodeToJson :: FilePath -> IO() encodeToJson srcName = do let jsonName = basename srcName <.> "json" contents <- readFile srcName let contentLines = lines contents case contentLines of -- head is unsafe! try your code on an empty file (firstLine : restLines) -> writeFile jsonName (encode Product { image = firstLine, description = unlines restLines }) _ -> print ("error: invalid source file: " ++ srcName)
main = do names <- listDirectory "." let srcNames = filter (flip hasExtension "src") names mapM_ encodeToJson srcNames
On Sun, Mar 1, 2015 at 6:47 PM Bram Neijt
wrote: Thank you all for the responses.
Qualified imports for a script of less then a page make the code less readable. That is why I want to specifically import subsets of functions and not prefix everything with qualified imports.
I've taken the code from Travis (clean up some of the useless pack/unpack), decided to use "NoImplicitPrelude" and then I had this:
#!/usr/bin/runghc {-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-} import Prelude (Show, IO, (++), print, String, filter) import Data.List (isSuffixOf) import Data.Text hiding (isSuffixOf, filter) import Data.Text.IO import Control.Monad (mapM_)
import qualified Data.ByteString.Lazy as BL import System.Directory (getDirectoryContents) import System.FilePath.Posix ((<.>), FilePath, takeBaseName)
import Data.Aeson
data Product = Product { image :: Text , description :: Text } deriving Show
instance ToJSON Product where toJSON (Product image description) = object ["image" .= image, "description" .= description]
encodeToJson :: FilePath -> IO() encodeToJson srcName = do let jsonName = takeBaseName srcName <.> "json" contents <- readFile srcName let contentLines = lines contents case contentLines of -- head is unsafe! try your code on an empty file (firstLine : restLines) -> BL.writeFile jsonName (encode Product { image = firstLine, description = unlines restLines }) _ -> print ("error: invalid source file: " ++ srcName)
main = do names <- getDirectoryContents "." let srcNames = filter (isSuffixOf ".src") names mapM_ encodeToJson srcNames
What bugs me is that this is a really simple thing to do in any other language, but here I seem to be stuck between Prelude and Text and as a beginner Prelude is king.
I tried ClassyPrelude but got stuck at not being able to get the result of getDirectoryContents into the ClassyPrelude version of mapM_. Sorry but I gave up on that.
I think that given all this, the above code is as beautiful as Haskell can pull this off at the moment.
If somebody can prove me wrong, I would love to still see it.
Greetings,
Bram
On Sun, Mar 1, 2015 at 1:42 AM, Travis Cardwell
wrote: On 2015年02月28日 20:23, Bram Neijt wrote:
It quickly became a collection of "import qualified" and "T.unpack, T.pack" calls that made the whole thing look ugly [1]. <SNIP> [1] Source can be read here: https://gist.github.com/bneijt/9bdb4b1759790a8463c9
File paths are of type `System.FilePath.Posix.FilePath`, a type alias for `String`. Note that this convention is also followed in `ByteString` and `Text` libraries; they do not use `ByteString` or `Text` types for the file paths.
In this code, file paths are packed only to have to unpack them again (twice!), which likely offsets any performance improvements of using the `Text` version of `isSuffixOf`.
Here is a version using the same style but without packing file paths: https://gist.github.com/TravisCardwell/fd9981e4968e4af3751d
I included a few other changes, which are commented in the code.
By the way, I do not think that qualified imports are bad. I like them because they provide an explicit reference to the source module of a function.
Cheers,
Travis _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
participants (3)
-
Bram Neijt
-
Michael Snoyman
-
Travis Cardwell