News aggregator

Use Haskell for shell scripting

Haskell on Reddit - Fri, 01/30/2015 - 12:56am
Categories: Incoming News

Gabriel Gonzalez: Use Haskell for shell scripting

Planet Haskell - Fri, 01/30/2015 - 12:19am

Right now dynamic languages are popular in the scripting world, to the dismay of people who prefer statically typed languages for ease of maintenance.

Fortunately, Haskell is an excellent candidate for statically typed scripting for a few reasons:

  • Haskell has lightweight syntax and very little boilerplate
  • Haskell has global type inference, so all type annotations are optional
  • You can type-check and interpret Haskell scripts very rapidly
  • Haskell's function application syntax greatly resembles Bash

However, Haskell has had a poor "out-of-the-box" experience for a while, mainly due to:

  • Poor default types in the Prelude (specifically String and FilePath)
  • Useful scripting utilities being spread over a large number of libraries
  • Insufficient polish or attention to user experience (in my subjective opinion)

To solve this, I'm releasing the turtle library, which provides a slick and comprehensive interface for writing shell-like scripts in Haskell. I've also written a beginner-friendly tutorial targeted at people who don't know any Haskell.

Overview

turtle is a reimplementation of the Unix command line environment in Haskell. The best way to explain this is to show what a simple "turtle script" looks like:

#!/usr/bin/env runhaskell

{-# LANGUAGE OverloadedStrings #-}

import Turtle

main = do
cd "/tmp"
mkdir "test"
output "test/foo" "Hello, world!" -- Write "Hello, world!" to "test/foo"
stdout (input "test/foo") -- Stream "test/foo" to stdout
rm "test/foo"
rmdir "test"
sleep 1
die "Urk!"

If you make the above file executable, you can then run the program directly as a script:

$ chmod u+x example.hs
$ ./example.hs
Hello, world!
example.hs: user error (Urk!)

The turtle library renames a lot of existing Haskell utilities to match their Unix counterparts and places them under one import. This lets you reuse your shell scripting knowledge to get up and going quickly.

Shell compatibility

You can easily invoke an external process or shell command using proc or shell:

#!/usr/bin/env runhaskell

{-# LANGUAGE OverloadedStrings #-}

import Turtle

main = do
mkdir "test"
output "test/file.txt" "Hello!"
proc "tar" ["czf", "test.tar.gz", "test"] empty

-- or: shell "tar czf test.tar.gz test" empty

Even people unfamiliar with Haskell will probably understand what the above program does.

Portability

"turtle scripts" run on Windows, OS X and Linux. You can either compile scripts as native executables or interpret the scripts if you have the Haskell compiler installed.

Streaming

You can build or consume streaming sources. For example, here's how you print all descendants of the /usr/lib directory in constant memory:

#!/usr/bin/env runhaskell

{-# LANGUAGE OverloadedStrings #-}

import Turtle

main = view (lstree "/usr/lib")

... and here's how you count the number of descendants:

#!/usr/bin/env runhaskell

{-# LANGUAGE OverloadedStrings #-}

import qualified Control.Foldl as Fold
import Turtle

main = do
n <- fold (lstree "/usr/lib") Fold.length
print n

... and here's how you count the number of lines in all descendant files:

#!/usr/bin/env runhaskell

{-# LANGUAGE OverloadedStrings #-}

import qualified Control.Foldl as Fold
import Turtle

descendantLines = do
file <- lstree "/usr/lib"
True <- liftIO (testfile file)
input file

main = do
n <- fold descendantLines Fold.length
print nException Safety

turtle ensures that all acquired resources are safely released in the face of exceptions. For example, if you acquire a temporary directory or file, turtle will ensure that it's safely deleted afterwards:

example = do
dir <- using (mktempdir "/tmp" "test")
liftIO (die "The temporary directory will still be deleted!")

However, exception safety comes at a price. turtle forces you to consume all streams in their entirety so you can't lazily consume just the initial portion of a stream. This was a tradeoff I chose to keep the API as simple as possible.

Patterns

turtle supports Patterns, which are like improved regular expressions. Use Patterns as lightweight parsers to extract typed values from unstructured text:

$ ghci
>>> :set -XOverloadedStrings
>>> import Turtle
>>> data Pet = Cat | Dog deriving (Show)
>>> let pet = ("cat" *> return Cat) <|> ("dog" *> return Dog) :: Pattern Pet
>>> match pet "dog"
>>> [Dog]
>>> match (pet `sepBy` ",") "cat,dog,cat"
[[Cat,Dog,Cat]]

You can also use Patterns as arguments to commands like sed, grep, find and they do the right thing:

>>> stdout (grep (prefix "c") "cat") -- grep '^c'
cat
>>> stdout (grep (has ("c" <|> "d")) "dog") -- grep 'cat\|dog'
dog
>>> stdout (sed (digit *> return "!") "ABC123") -- sed 's/[[:digit:]]/!/g'
ABC!!!

Unlike many Haskell parsers, Patterns are fully backtracking, no exceptions.

Formatting

turtle supports typed printf-style string formatting:

>>> format ("I take "%d%" "%s%" arguments") 2 "typed"
"I take 2 typed arguments"

turtle even infers the number and types of arguments from the format string:

>>> :type format ("I take "%d%" "%s%" arguments")
format ("I take "%d%" "%s%" arguments") :: Text -> Int -> Text

This uses a simplified version of the Format type from the formatting library. Credit to Chris Done for the great idea.

The reason I didn't reuse the formatting library was that I spent a lot of effort keeping the types as simple as possible to improve error messages and inferred types.

Learn more

turtle doesn't try to ambitiously reinvent shell scripting. Instead, turtle just strives to be a "better Bash". Embedding shell scripts in Haskell gives you the the benefits of easy refactoring and basic sanity checking for your scripts.

You can find the turtle library on Hackage or Github. Also, turtle provides an extensive beginner-friendly tutorial targeted at people who don't know any Haskell at all.

Categories: Offsite Blogs

Haskell with s-expression syntax?

Haskell on Reddit - Thu, 01/29/2015 - 7:59pm

Hello all, I really love the succinct expressiveness Haskell gives me. I especially love the strong typing and high level abstractions Haskell exposes. I do, however, prefer the s-expression syntax of the lisp family. Syntax may seem unimportant, and the macro capabilities are usually boasted as lisp's strong suit (though I feel you don't need them as much with Haskell's higher level abstractions), the syntax is so simple and beautiful IMO.

I would like to make, or find, a lisp with Haskell's strong typing and high level abstractions. Does anyone know of any actively developed projects in this vein? Or maybe the ability to write Haskell directly as an AST?

submitted by briticus557
[link] [22 comments]
Categories: Incoming News

Is it possible to communicate with a Haskell/GHCI REPL in a client + server fashion, like with the Clojure nREPL?

Haskell on Reddit - Thu, 01/29/2015 - 6:30pm

I would like to write a program that throws strings of Haskell code at a process, and have that process evaluate the strings, just as if I were typing into a GHCI. :)

Clojure nREPL = https://github.com/clojure/tools.nrepl

Cheers, Louis

submitted by MrPopinjay
[link] [7 comments]
Categories: Incoming News

Haskell and Web

del.icio.us/haskell - Thu, 01/29/2015 - 4:15pm
Categories: Offsite Blogs

Haskell and Web

del.icio.us/haskell - Thu, 01/29/2015 - 4:15pm
Categories: Offsite Blogs

foobarcode.me

del.icio.us/haskell - Thu, 01/29/2015 - 2:10pm
Categories: Offsite Blogs

Roman Cheplyaka: Recognizing lists

Planet Haskell - Thu, 01/29/2015 - 2:00pm
import Data.Monoid import Data.List (isSuffixOf) import Control.Applicative import Control.Applicative.Permutation as Perm import Control.Monad.Trans.State

You are given the following Haskell data type:

data T = A Int | B String

Your task is to write a function

recognize :: [T] -> Bool

that would accept a list iff it contains exactly three elements, of which:

  1. one is an A with a value between 3 and 7
  2. one is an A with an even value
  3. one is a B with a value ending in .hs

The A elements mentioned in conditions (1) and (2) have to be distinct, so [A 4, A 17, B "Prelude.hs"] should not be accepted, even though A 4 satisfies both (1) and (2).

This problem often arises in blackbox testing. You do something to a system; you wait for some time while gathering all events that the system generates. Then you want to check that the system generated all the events it had to generate, and no extra ones. However, the order of events may be non-deterministic. If the system concurrently makes a web request, updates a database and sends a message to a message bus, the order in which these events will be observed by your test suite is arbitrary.

A similar problem arises in input validation.

Interface

We want to solve this problem in a general and scalable way. What would an interface to such solution look like? There are two principal options.

One is a simple function

recognize :: [t -> Bool] -> [t] -> Bool

that takes individual predicates and combines them into a recognizer.

Another is to split the process into the build and use phases by the means of an intermediate data type:

data Parser t instance Monoid Parser toParser :: (t -> Bool) -> Parser t runParser :: Parser t -> [t] -> Bool

In this approach, you convert individual recognizers to monoidal values, combine those values and extract the result.

Semantically, the two are equivalent: if you have one of these interfaces available, you can implement the other one. In a way, this is a consequence of lists being the free monoid, but writing this down explicitly is a good exercise nevertheless.

The advantages of a build-use interface are:

  1. When the cost of the build phase is non-trivial, it can be amortized over multiple uses and/or incremental builds. (This won’t be the case here.)
  2. The use of a specialized type is generally good for the type system hygiene.

We’ll start with the build-use interface because I find it more instructive.

The Parser

To build the monoidal parser, we’ll use the action-permutations library. You may remember it from the article on JSON parsing.

newtype Parser t = Parser { getParser :: Perms (StateT [t] Maybe) () }

StateT [t] Maybe a is the familiar type of parser combinators, [t] -> Maybe (a, [t]), that return the left-over input. We wrap it in Perms to achieve the order independence, and then newtype it to convert its Applicative instance to a Monoid one:

instance Monoid (Parser t) where mempty = Parser (pure ()) mappend (Parser a) (Parser b) = Parser $ a *> b

The build and use functions are straightforward:

toParser :: (t -> Bool) -> Parser t toParser isX = Parser $ Perm.atom $ StateT $ \ts -> case ts of (t:ts') | isX t -> Just ((), ts') _ -> Nothing runParser :: Parser t -> [t] -> Bool runParser p ts = case (flip execStateT ts . runPerms . getParser) p of Just [] -> True _ -> False Usage

First, let’s express our conditions in Haskell code.

isA1, isA2, isB :: T -> Bool isA1 x | A n <- x, 3 <= n, n <= 7 = True | otherwise = False isA2 x | A n <- x, even n = True | otherwise = False isB x | B s <- x, ".hs" `isSuffixOf` s = True | otherwise = False

Combine them together:

parser :: Parser T parser = (mconcat . map toParser) [isA1, isA2, isB]

(we could replace mconcat . map toParser with foldMap toParser from Data.Foldable)

recognize :: [T] -> Bool recognize = runParser parser

Now try it out:

ex1 = recognize [A 4, A 17, B "Prelude.hs"] -- => False ex2 = recognize [A 4, A 18, B "Prelude.hs"] -- => True ex3 = recognize [B "Prelude.hs", A 18, A 4] -- => True ex4 = recognize [B "Prelude.hs", A 18, A 4, A 1] -- => False
Categories: Offsite Blogs

JP Moresmau: EclipseFP 2.6.4 released!

Planet Haskell - Thu, 01/29/2015 - 11:13am
Hello all. I've just released EclipseFP 2.6.4, to provide a fix for people that could not create projects due to a NullPointerException. Unfortunately I am at the moment the only contributor to EclipseFP which means nobody else than myself tests it before a release, so regressions happen. I would love to see more people contribute and build from source!

A couple of fixes and small enhancements have been made, the release note are here.

Just update by pointing your Eclipse update feature to http://eclipsefp.sf.net/updates.

Happy Haskell Hacking!
Categories: Offsite Blogs

York Haskell Meetup TONIGHT

Haskell on Reddit - Thu, 01/29/2015 - 8:57am
Categories: Incoming News

Tranversable instance for Trie

Haskell on Reddit - Thu, 01/29/2015 - 8:33am

Hey!

I've got the following data type for Trie:

data Trie a = Trie { value :: Maybe a , children :: M.Map Char (Trie a) } deriving (Show)

How would I go about making a transversable instance for this data type? So far I have:

instance Traversable Trie where traverse f t | isJust (value t) = Trie {value = f (value t), children = traverse (traverse f) (children t)} | otherwise = Trie {value = Nothing, children = traverse (traverse f) (children t)}

Which is not working and I get the error:

C:\trie.hs:24:82: Couldn't match type `M.Map Char' with `Trie' Expected type: Trie (Trie a) Actual type: M.Map Char (Trie a) In the second argument of `traverse', namely `(children t)' In the `children' field of a record C:\Users\Matias\Downloads\trie.hs:26:78: Couldn't match type `M.Map Char' with `Trie' Expected type: Trie (Trie a) Actual type: M.Map Char (Trie a) In the second argument of `traverse', namely `(children t)' In the `children' field of a record Failed, modules loaded: none. submitted by GrimBeast
[link] [6 comments]
Categories: Incoming News

Recognizing lists

Haskell on Reddit - Thu, 01/29/2015 - 7:11am
Categories: Incoming News

Any Haskell enthusiasts from Bangalore India?

Haskell on Reddit - Thu, 01/29/2015 - 4:17am

I am a newbie to Haskell programming. I would like to know if there are any Haskell enthusiasts that would like to work on Haskell projects. My focus is currently on learning Haskell and eventually develop side projects in Haskell.

submitted by vrbala
[link] [11 comments]
Categories: Incoming News

www.macs.hw.ac.uk

del.icio.us/haskell - Thu, 01/29/2015 - 3:30am
Categories: Offsite Blogs