понедельник, 12 июня 2017 г.

Consistent application of mapM for Streaming/Pipes items

This is a Gabriel Gonzalez answer about combining of several mapM/filters with Pipes, Streaming, etc libraries:

---cut---

I think it's important to distinguish between two separate concepts: "fusion" vs "one-pass".  "Fusion" refers to avoiding the allocation of intermediate data structures when you transform a stream multiple times whereas "one-pass" means that you don't traverse the sequence of elements twice when you transform the stream multiple times (i.e. you go over the stream in one pass).  You can have a "one-pass" implementation without "fusion" but you cannot have "fusion" without a "one-pass" implementation.
"Fusion" is purely an optimization, meaning that whether or not an implementation uses "fusion" only affects your program's performance but won't affect its behavior.  However, "one-pass" is not just an optimization: one-pass versus multiple pass changes the behavior of your program, especially once your stream has effects like in these streaming libraries.
Out of the two properties, "one-pass" is *much* more important.  The reason why is that "one-pass" ensures that certain functor laws hold.  To see why, let's consider a case where they *don't* hold, which is `Data.List.mapM`.  Normally, you mtigh expect the following functor laws to hold:
    Data.List.mapM (f <=< g) = Data.List.mapM f . Data.List.mapM g
    Data.List.mapM return = id

However, the above two laws don't actually hold for `Data.List.mapM`.  For example, the first law does not hold because the order of effects are not the same for the left-hand and right-hand sides of the equation.  The left-hand side of the equation interleaves the effects of `f` and `g` whereas the right-hand side runs all of `g`'s effects first followed by all of `f`'s effects.  The second equation is also wrong because `Data.List.mapM` misbehaves on infinite lists:
    Data.List.mapM return (repeat x) = _|_
    id (repeat x) = repeat x

This is the root of why `Data.List.mapM` is "bad"
However, the streaming libraries have their own versions of `mapM` which do obey the above functor laws.  For example, if you take the `list-transformer` library and define:

    mapM :: (a -> m b) -> ListT m a -> ListT m b
    mapM f as = do
        a <- as="" br="">
        lift (f a)
 
... then this *does* obey the following functor laws:

    mapM (f <=< g) = mapM f . mapM g
    mapM return = id
For the first equation, both sides of the equation interleave the effects of `f` and `g`.  For the second equation, both sides of the equation behave correctly on infinite `ListT` streams.  These functor laws hold because `ListT` is has the "one-pass" property.
So to answer your question: it's not exactly the Haskell `Functor` type class per se that is important here, but the functor laws are important (for a more general notion of functor) in establishing why a single pass implementation matters.

---cut---

Thanks, Gabriel!

четверг, 20 апреля 2017 г.

Parse date in free format from JSON with Aeson

Already is night, so post will be short :) This is an example how to parse JSON data (date stamp) in free format:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import           Control.Monad        (mzero)
import           Data.Aeson
import qualified Data.ByteString.Lazy as B
import           Data.Text
import           Data.Time
import           Data.Maybe (fromJust)

main :: IO ()
main = getJSON >>= print

data Person =
  Person {  name  :: !Text
          , age   :: Int
          , birth :: UTCTime
            } deriving Show

prsTime :: String -> UTCTime
prsTime = fromJust . parseTimeM True defaultTimeLocale "%0Y,%m"

instance FromJSON Person where
  parseJSON (Object v) =
      Person <$> v .: "name"
             <*> v .: "age"
             <*> (prsTime <$> v .: "birth")
  parseJSON _ = mzero


jsonFile :: FilePath
jsonFile = "js.json"

getJSON :: IO (Maybe Person)
getJSON = decode <$> B.readFile jsonFile

To build I change .cabal file to:

...
build-depends:       base
                     , js
                     , text
                     , aeson
                     , bytestring >= 0.10
                     , time
...

Our testing JSON file D:\prj\js\js.json will be:

{
    "name": "alex",
    "age": 20,
    "birth": "2017,10"
}

so, as you can see our date has format "YYYY,mm". Build and run as usual:

D:\prj\js> stack build
D:\prj\js> stack exec js-exe
Just (Person {name = "alex", age = 20, birth = 2017-10-01 00:00:00 UTC})

пятница, 14 апреля 2017 г.

Linking HDBC/Sqlite3 on Haskell stack under Windows

To link your Haskell application with HDBC for Sqlite3 under Windows, you have to:

install sqlite3 dev C package (headers and libs)

  • go to your Haskell MSYS2 installation (for example, D:\apps\haskell\8.0.2\msys\) and run msys2.exe
  • in opened terminal run
pacman -Syu
# if needed - close terminal and run again
pacman -Su
pacman -S libsqlite-devel
pacman -S sqlite # to have CLI tool

modify cabal and stack.yaml files

  • add to stack.yaml:
...
extra-deps: [HDBC-sqlite3-2.3.3.1]
...
extra-include-dirs: ["d:/apps/haskell/8.0.2/msys/usr/include"]
extra-lib-dirs: ["d:/apps/haskell/8.0.2/msys/usr/lib"]
...
  • add to cabal file:
  build-depends:       base >= 4.7 && < 5
                     , HDBC
                     , HDBC-sqlite3
  • now you can import modules:
import Database.HDBC
import Database.HDBC.Sqlite3
...

Man page ASCII output tags

Simple way to output man page in ASCII only format (no any special symbols):

groff -P\-c -mandoc -Tascii file.1|col -bx > file.txt

file.txt is formatted as you see man output on your screen.