News aggregator

Neil Mitchell: HLint now spots bad unsafePerformIO

Planet Haskell - Sun, 10/19/2014 - 3:23pm

Summary: I've just released a new version of HLint that can spot an unsafePerformIO which should have NOINLINE but doesn't.

I've just released HLint v1.9.10, a tool to suggest improvements to Haskell code. I don't usually bother with release announcements of HLint, as each version just fixes a few things and adds a few hints, it's all in the changelog (plus there have now been 102 releases). The latest release attempts to make using unsafePerformIO a little bit safer. A common idiom for top-level mutable state in Haskell is:

myGlobalVar :: IORef Int
myGlobalVar = unsafePerformIO (newIORef 17)

That is, define a top-level CAF (function with no variables) and bind it to unsafePerformIO of some created mutable state. But the definition above is unsafe. GHC might decide myGlobalVar is cheap and inline it into several uses, duplicating the variable so that some functions update different versions. Running this code through the latest version of HLint we see:

Sample.hs:2:1: Error: Missing NOINLINE pragma
myGlobalVar = unsafePerformIO (newIORef 17)
Why not:
{-# NOINLINE myGlobalVar #-}
myGlobalVar = unsafePerformIO (newIORef 17)

HLint has spotted the problem, and suggested the correct fix.

Trying it for real

Let's take the package slave-thread-0.1.0 and run HLint on it. Slave thread is a new package that helps you ensure you don't end up with ghost threads or exceptions being thrown but missed - a useful package. Running HLint we see:

Sample.hs:19:1: Error: Missing NOINLINE pragma
slaves = unsafePerformIO $ Multimap.newIO
Why not:
{-# NOINLINE slaves #-}
slaves = unsafePerformIO $ Multimap.newIO

Sample.hs:20:3: Warning: Redundant $
unsafePerformIO $ Multimap.newIO
Why not:
unsafePerformIO Multimap.newIO

Sample.hs:25:1: Error: Eta reduce
fork main = forkFinally (return ()) main
Why not:
fork = forkFinally (return ())

Sample.hs:55:28: Warning: Redundant $
PartialHandler.totalizeRethrowingTo_ masterThread $ mempty
Why not:
PartialHandler.totalizeRethrowingTo_ masterThread mempty

Sample.hs:72:5: Error: Use unless
if null then return () else retry
Why not:
Control.Monad.unless null retry

HLint has managed to spot the missing NOINLINE pragma, and also a handful of tiny cleanups that might make the code a little more readable. Fortunately (and independent of HLint), the NOINLINE pragma was added in slave-thread-0.1.1, so the library no longer suffers from that bug.

Categories: Offsite Blogs

FilePath modification proposal

libraries list - Sun, 10/19/2014 - 1:35pm
Hi, As maintainer, it's kind of a grey area if I need to make proposals for the filepath library, but since I'm a relatively new active maintainer for a Core library, I thought I'd err on the side of caution. Please yell if you think these are a bad idea, otherwise I'll just do them (probably over the next few weeks). * Change 1: Add -<.> as an alias for replaceExtension with the same precedence as <.> This change has been in Shake for a while now, and seems generally useful. It isn't an operator in the lens library, and it reads nicely as a combination of - (remove something) and <.> (addExtension). The argument order of replaceExtension is a bit unfortunately, since replace functions usually have the thing to replace with first, but since extensions are at the end of the FilePaths that suggests making it the second argument (which is what filepath does). In contrast, -<.> has a very obvious argument order. * Change 2: Stop testing as part of GHC The test suite is complicated enough, since it generates
Categories: Offsite Discussion

Recommended way to install,use and maintain haskell tools on OS X

haskell-cafe - Sun, 10/19/2014 - 12:42pm
Hi I know this question might be a little old, but I happen to have a from scratch installation of OS X (yosemite) on my machine and I want to install the haskell tools in the currently best way. So I summarize the options that I understand are available: 1) A non-option first: the Homebrew formula for haskell-platform is no longer available, but… 2) There are formulas for GHC (7.8.x) and cabal, so I can install those and install the other packages with the homebrew cabal. 3) I can install the binary distribution of the Haskell Platform for OS X directly from the website, and then install any needed package with cabal. 4) I’ve heard of Nix, what is this all about? 5) Installing manually the binary distributions of ghc and cabal. No thanks. That said, what is the recommended way to manage packages once I have cabal and ghc? 1) Installing everything locally into ~/Library/Haskell, which afaik is the default behavior of cabal. That also means having ~/Library/Haskell/bin in the PATH. But do I have to a
Categories: Offsite Discussion

Future of "Text.Show.Functions" module

libraries list - Sun, 10/19/2014 - 11:20am
Hello *, I noticed the following module (trimmed for brevitiy) hiding in `base`: -- This module deliberately declares orphan instances: {-# OPTIONS_GHC -fno-warn-orphans #-} -- | Optional instance of 'Text.Show.Show' for functions: -- -- > instance Show (a -> b) where -- > showsPrec _ _ = showString \"\<function\>\" -- ----------------------------------------------------------------------------- module Text.Show.Functions () where instance Show (a -> b) where showsPrec _ _ = showString "<function>" However, I consider this a questionable module to be in `base` due to its deliberate use of orphan instances. Should this module be deprecated, removed, alternatively, should the `Show` instance be made a non-orphan (e.g. by importing it by the `Prelude` module), or shall this curiousity be just left untouched in `base` in its current form? Cheers, hvr
Categories: Offsite Discussion

Homebrew cabal-install broken on OSX Yosemite?

Haskell on Reddit - Sun, 10/19/2014 - 9:10am

Is it just me or is the cabal-install package from Homebrew broken?

$ brew info cabal-install cabal-install: stable (bottled) Not installed From: ==> Dependencies Required: ghc ✔ $ brew install cabal-install ==> Downloading ######################################################################## 100.0% ==> Pouring cabal-install- ==> Caveats Bash completion has been installed to: /usr/local/etc/bash_completion.d ==> Summary
Categories: Incoming News

Matthew Sackman: Anonymity in public life

Planet Haskell - Sun, 10/19/2014 - 9:00am

In an article published in the Guardian yesterday, author Kathleen Hale recounts how her first book got some negative reviews by reviewers on a book review website. One reviewer in particular upset her and Kathleen ends up figuring out the reviewer is using a false identity, finds out who the reviewer really is and confronts her. The piece doesn't read to me like some sort of valedictory "I outed a fraud" type piece (though there are some passages in there which are questionable in that direction) and equally there are several passages where Kathleen expresses deep embarrassment and regret for the course of action she took. This episode, and that article in particular has caused substantial reaction: currently 600 comments on the Guardian article plus several other blog posts. There's no shortage of opinion to be found on Twitter either, as you'd expect.

The course of action that Kathleen took seems to be fairly undisputed as far as I can find. There is some dispute from some of the other blog posts as to exactly what was tweeted and said by whom, and there is dispute over Kathleen's claim that there are factual inaccuracies made in a review of her book. It is not disputed that the reviewer was using a false identity and that the reviewer had at least public Twitter, Facebook, and Instagram accounts under the false identity. The false identity was also a real name (Blythe Harris), by which I mean a name which if you introduced yourself by that name, no one would think you're using a false identity. This is distinct from claiming to be Peter Rabbit, or Buzz Lightyear.

Many people have equated Kathleen's actions with stalking. My dictionary defines the verb to stalk as:

  1. to follow or approach (game, prey, etc.) stealthily and quietly
  2. to pursue persistently and, sometimes, attack (a person with whom one is obsessed, often a celebrity)
  3. , 4,... [not relevant]

The second item there certainly fits. The British legal approach, whilst it gives no strict definition gives examples and guidance:

....following a person, watching or spying on them or forcing contact with the victim through any means, including social media.

The effect of such behaviour is to curtail a victim's freedom, leaving them feeling that they constantly have to be careful. In many cases, the conduct might appear innocent (if it were to be taken in isolation), but when carried out repeatedly so as to amount to a course of conduct, it may then cause significant alarm, harassment or distress to the victim.

I'm glad it includes "social media" there. Some comments have suggested that stalking "in real life" is worse than online. This seems bizarre to me: as if through a computer you are not interacting with other human beings but merely with shiny pixels who have no emotional capacity. "In real life" is everything we know. Whilst we're alive we have no personal experience of anything other than "in real life".

So I'm fairly sold on the whole argument that Kathleen's behaviour towards this reviewer can be considered stalking and as such is reprehensible.

To me, the far more interesting issue is the use of anonymity, false identities and any realistic expectation we have of privacy on the internet. A number of people who claim to write book reviews on such sites have suggested that the behaviour of Kathleen is exactly why they write their reviews under false names. I think there's something of a contradiction going on here.

But let's work backwards. Firstly, Kathleen, through some social engineering (she requested from the book review site the address of the reviewer so that she could post her a copy of the book) got the address of the book reviewer. She then used a telephone directory and census results to identify who really lived there (or likely owned the land). Now the use of the telephone directory seems a bit odd to me: telephony directories map names to numbers (and maybe addresses). Yes, you could use it to map an address to a name but it's very inefficient: you're essentially searching through the whole directory looking for the address whilst the directory is sorted by name, not address. So unless it was a very small telephone directory, I don't really buy that. Using census results is far more creditable: they're public documents and when they're online, they do allow you to search by address. In the UK you can only get access to the raw census details 100 years after the census has been published which, to a high probability, rules it out as a means to tie an address to a person who's still alive. You can get statistics and aggregates from more recent census results but you can't get the raw data. I'm assuming that in the US there's no such restriction on access to raw census data. If there is then I don't understand how Kathleen really managed to get a name for the owner of the property.

Instead, in the UK, if you want to find out who owns some land, you can pay the land registry £3 and they'll tell you. Presumably there are means by which you can legally hide this; I'm sure the rich have figured this out - probably some method by which some fake company in a tax haven technically "owns" the land and as they're registered abroad, they don't have to divulge any further details about that company. So yes, you could argue the Land Registry is profiting from facilitating stalkers, but equally there are a bunch of legitimate reasons to need access to such data and I can't think of any sane way to ensure the use of such a service isn't abused. So from that I conclude that unless the owner is a millionaire, the owner of any land is public knowledge.

The use of social engineering to get the address in the first place is more interesting but very obvious. This sort of thing happens a lot and sometimes to horrifying consequences (e.g. the Australian DJs who phoned up a hospital, pretending to be the Queen and Prince of Wales, enquiring as to the health of the Duchess of Cambridge. The nurse fell for the hoax and put the call through. Three days later, the nurse committed suicide). As a species we are not good at taking the time to verify who we're talking to or why. Whilst (hopefully) most of us would hang up if our bank apparently rang us and then asked for our credit card details "for security" this is largely only because it's in the bank's interest (in terms of cost of insurance) to reduce fraud, so they've trained us as such. But in all sorts of other scenarios we implicitly trust people we've no real reason to. A simple example: ticket inspectors on public transport. They may be wearing the uniform, but it could be faked. With their travel-card readers they could be seeing who has the expensive yearly travel cards, scanning the unique numbers from them and then using them to program up fraudulent cards. The crypto on those things is notoriously weak. Has anyone ever requested some means to verify the identity of a ticket inspector? And even if you could, how do you know they're not crooked regardless?

So phoning someone up, impersonating someone else, or pretending to have valid reasons to request the information you're requesting is always likely to work. It might be illegal in some cases, but it's certainly human nature to try to be helpful and if you're given a plausible justification, on what basis could you refuse the request unless it's contrary to some sort of company policy? In this case, if you're concerned about anonymity, wouldn't you be concerned about this possibility, and make use of an anonymous mail box?

Article 8 of the European Convention on Human Rights guarantees an individual's right to respect for privacy and family life, including correspondence. Is privacy the same as anonymity? No, definitely not:

In conflating anonymity and privacy, we have failed to see an important factual difference between them: under the condition of privacy, we have knowledge of a person’s identity, but not of an associated personal fact; whereas under the condition of anonymity, we have knowledge of a personal fact, but not of the associated person’s identity

The vast violations of our lives by state surveillance as revealed by Snowdon over the last year demonstrates the whole-scale collation of everything we do online and off by our governments. This is both being able to observe an action and identify the individual who caused it (thus we have no hope of performing any action anonymously), and being able to observe an individual and know the actions they take (thus no privacy). I can't work out whether the ECHR has anything to say on a right to anonymity; I get the sense that it doesn't try to protect that. So that's basically saying: "the state shouldn't record your every move (as that's an invasion of privacy), but moves that we're interested in, we can know who did them". Of course, we now know they're recording everything anyway.

We also know that computer systems can always be hacked into - there is no real security anywhere. Given a skilled and sufficiently funded adversary, any computer system connected in any way to the internet can be hacked into. Why? Because humans wrote the software that runs on those computers and humans are incapable of writing bug-free software. Look at all the large scale data breaches in recent history. Nothing is secure.

So we have laws that seem to try and protect privacy, but they're violated by our own governments, and in any case, we have countless examples of our inability to store any information securely. So is there really any hope to be able to exist with anonymity on the internet?

As ever, it depends who your adversary is. If your adversary is a government (either your own or some foreign government) then no, you have no hope. If it's a previous partner of yours who has no particular computer training, then yes, you're probably going to have a reasonable chance of being anonymous for a while. But you need to read up on this and think hard: it's not a trivial undertaking. There are some good guides as to how to do this, but:

All writers - whether writing under their own names or not - should be aware of the risks they may incur by hitting 'publish'.

What is the effect of hitting "publish"? It's to put more data points out there which may lead people to be able to identify you. The fewer data points out there, the better. So coming back to our book reviewer, if you want to review books anonymously, and if your justification for acting anonymously is to avoid being stalked by authors who don't like your reviews, then why put so many data points out there? Why have the Facebook page, the Instagram profile with the faked photos, the Twitter account? Why give your real postal address to the book review club knowing they're going to post books to it and might conceivably give your address out to other people?

The social media accounts in particular I find most odd. If you want to review books then review books. Build your following, your reputation and cachet on the quality of your reviews. If I'm looking at a book review I really don't care where you went on holiday, what your tweets are, or how many pets you have. Putting that information out there undermines your entire justification for being anonymous: if you want to be anonymous (i.e. you don't want people to find out who you are) then why are you putting so much unnecessary information out there that may allow people to figure out who you are?

Equally, use a name that clearly communicates to me you're trying to be anonymous: call yourself TheBookReviewer53, DostoyevskyLover or OrwellWasRight. Doing so doesn't lessen the validity of your opinions on your chosen subject and is more honest with people reading your reviews: it's overtly saying "I have reasons to want to exist anonymously on the internet". It reveals nothing more about your real identity either: regardless of the obvious fictitious-ness of your online persona, if you can be found, you can be found.

Researchers show that four data points about a person’s location can identify that person with 95% accuracy. FOUR. You think you can tweet anonymously from your phone? You think apps like Whisper allow you to act anonymously? As with pretty much everything related to the internet and computing, unless you've spent the last 20 years of your life working with computers, studying computers and thinking very hard about threat models and what data you're putting out there, and are utterly paranoid, you basically haven't got a chance. Do you turn off wifi on your phone when you leave the house? You should. You trust that USB pen drive you're transferring documents on? You shouldn't.

Finally and most obviously, any attempt at anonymity clearly doesn't insulate you from the law. As members of various hacking groups such as lulzsec found out, you always can be found out by law enforcement agencies. Yes, you might be able to make it difficult for a poorly funded person to come after you for libel (which is really just an indictment of the disgusting relationship between justice and money) but it's quite a risk to take. If you wouldn't put it in print with your real name attached, you're placing an awful lot of trust on your ability to maintain your anonymity against an adversary you probably don't know as well as you need to.

Categories: Offsite Blogs

Well-Typed.Com: Quasi-quoting DSLs for free

Planet Haskell - Sun, 10/19/2014 - 8:01am

Suppose you are writing a compiler for some programming language or DSL. If you are doing source to source transformations in your compiler, perhaps as part of an optimization pass, you will need to construct and deconstruct bits of abstract syntax. It would be very convenient if we could write that abstract syntax using the syntax of your language. In this blog post we show how you can reuse your existing compiler infrastructure to make this possible by writing a quasi-quoter with support for metavariables. As we will see, a key insight is that we can reuse object variables as meta variables.

Toy Language “Imp”

For the sake of this blog post we will be working with a toy language called Imp. The abstract syntax for Imp is defined by

type VarName = String data Expr = Var VarName | Add Expr Expr | Sub Expr Expr | Int Integer | Read deriving (Data, Typeable, Show, Eq) data Cmd = Write Expr | Assign VarName Expr | Decl VarName deriving (Data, Typeable, Show) data Prog = Prog [Cmd] deriving (Data, Typeable, Show)

and we will assume that we have some parsec parsers

parseExpr :: Parser Expr parseProg :: Parser Prog

We will also make use of

topLevel :: Parser a -> Parser a topLevel p = whiteSpace *> p <* eof

and the following useful combinator for running a parser:

parseIO :: Parser a -> String -> IO a

The details of these parsers are beyond the scope of this post. There are plenty of parsec tutorials online; for instance, you could start with the parsec chapter in Real World Haskell. Moreover, the full code for this blog post, including a simple interpreter for the language, is available on github if you want to play with it. Here is a simple example of an Imp program:

var x ; x := read ; write (x + x + 1) A simple quasi-quoter

We want to be able to write something like

prog1 :: Prog prog1 = [prog| var x ; x := read ; write (x + x + 1) |]

where the intention is that the [prog| ... |] quasi-quote will expand to something like

prog1 = Prog [ Decl "x" , Assign "x" Read , Write (Add (Add (Var "x") (Var "x")) (Int 1)) ]

To achieve this, we have to write a quasi-quoter. A quasi-quoter is an instance of the following data type:

data QuasiQuoter = QuasiQuoter { quoteExp :: String -> Q Exp , quotePat :: String -> Q Pat , quoteType :: String -> Q Type , quoteDec :: String -> Q [Dec] }

The different fields are used when using the quasi-quoter in different places in your Haskell program: at a position where we expect a (Haskell) expression, a pattern (we will see an example of that later), a type or a declaration; we will not consider the latter two at all in this blog post.

In order to make the above example (prog1) work, we need to implement quoteExp but we can leave the other fields undefined:

prog :: QuasiQuoter prog = QuasiQuoter { quoteExp = \str -> do l <- location' c <- runIO $ parseIO (setPosition l *> topLevel parseProg) str dataToExpQ (const Nothing) c , quotePat = undefined , quoteType = undefined , quoteDec = undefined }

Let’s see what’s going on here. The quasi-quoter gets as argument the string in the quasi-quote brackets, and must return a Haskell expression in the Template-Haskell Q monad. This monad supports, amongst other things, getting the current location in the Haskell file. It also supports IO.


The first thing that we do is find the current location in the Haskell source file and convert it to parsec format:

location' :: Q SourcePos location' = aux <$> location where aux :: Loc -> SourcePos aux loc = uncurry (newPos (loc_filename loc)) (loc_start loc) Running the parser

Once we have the location we then parse the input string to a term in our abstract syntax (something of type Prog). We use parsec’s setPosition to tell parsec where we are in the Haskell source file, so that if we make a mistake such as

prog1 :: Prog prog1 = [prog| var x ; x := read ; write (x + x + ) |]

we get an error that points to the correct location in our Haskell file:

TestQQAST.hs:6:9: Exception when trying to run compile-time code: user error ("TestQQAST.hs" (line 9, column 20): unexpected ")" expecting "(", "read", identifier or integer) Converting to Haskell abstract syntax

The parser returns something of type Prog, but we want something of type Exp; Exp is defined in Template Haskell and reifies the abstract syntax of Haskell. For example, we would have to translate the Imp abstract syntax term

Var "x" :: Prog

to its reflection as a piece of abstract Haskell syntax as

AppE (ConE 'Var) (LitE (StringL "x")) :: TH.Exp

which, when spliced into the Haskell source, yields the original Prog value. Fortunately, we don’t have to write this translation by hand, but we can make use of the following Template Haskell function:

dataToExpQ :: Data a => (forall b. Data b => b -> Maybe (Q Exp)) -> a -> Q Exp

This function can translate any term to a reified Haskell expression, as long as the type of the term derives Data (Data instances can be auto-derived by ghc if you enable the DeriveDataTypeable language extension). The first argument allows you to override the behaviour of the function for specific cases; we will see an example use case in the next section. In our quasi-quoter so far we don’t want to override anything, so we pass a function that always returns Nothing.

Once we have defined this quasi-quoter we can write

prog1 :: Prog prog1 = [prog| var x ; x := read ; write (x + x + 1) |]

and ghc will run our quasi-quoter and splice in the Haskell expresion corresponding to the abstract syntax tree of this program (provided that we enable the QuasiQuotes language extension).


Consider this function:

prog2 :: VarName -> Integer -> Prog prog2 y n = [prog| var x ; x := read ; write (x + y + n) |]

As mentioned, in the source code for this blog post we also have an interpreter for the language. What happens if we try to run (prog2 "x" 1)?

*Main> intIO $ intProg (prog2 "x" 2) 5 *** Exception: user error (Unbound variable "y")

Indeed, when we look at the syntax tree that got spliced in for prog2 we see

Prog [ Decl "x" , Assign "x" Read , Write (Add (Add (Var "x") (Var "y")) (Var "n")) ]

What happened? Didn’t we pass in "x" as the argument y? Actually, on second thought, this makes perfect sense: this is after all what our string parses to. The fact that y and n also happen to Haskell variables, and happen to be in scope at the point of the quasi-quote, is really irrelevant. But we would still like prog2 to do what we expected it to do.

Meta-variables in Template Haskell

To do that, we have to support meta variables: variables from the “meta” language (Haskell) instead of the object language (Imp). Template Haskell supports this out of the box. For example, we can define

ex :: Lift a => a -> Q Exp ex x = [| id x |]

Given any argument that supports Lift, ex constructs a piece of abstract Haskell syntax which corresponds to the application of the identity function to x. (Don’t confuse this with anti-quotation; see Brief Intro to Quasi-Quotation.) Lift is a type class with a single method

class Lift t where lift :: t -> Q Exp

For example, here is the instance for Integer:

instance Lift Integer where lift x = return (LitE (IntegerL x)) Meta-variables in quasi-quotes

Quasi-quotes don’t have automatic support for meta-variables. This makes sense: Template Haskell is for quoting Haskell so it has a specific concrete syntax to work with, where as quasi-quotes are for arbitrary custom syntaxes and so we have to decide what the syntax and behaviour of meta-variables is going to be.

For Imp we want to translate any unbound Imp (object-level) variable in the quasi-quote to a reference to a Haskell (meta-level) variable. To do that, we will introduce a similar type class to Lift:

class ToExpr a where toExpr :: a -> Expr

and provide instances for variables and integers:

instance ToExpr VarName where toExpr = Var instance ToExpr Integer where toExpr = Int

We will also need to know which variables in an Imp program are bound and unbound; in the source code you will find a function which returns the set of free variables in an Imp program:

fvProg :: Prog -> Set VarName Overriding the behaviour of dataToExpQ

In the previous section we mentioned that rather than doing the Prog -> Q Exp transformation by hand we use the generic function dataToExpQ to do it for us. However, now we want to override the behaviour of this function for the specific case of unbound Imp variables, which we want to translate to Haskell variables.

Recall that dataToExpQ has type

dataToExpQ :: Data a => (forall b. Data b => b -> Maybe (Q Exp)) -> a -> Q Exp

This is a rank-2 type: the first argument to dataToExpQ must itself be polymorphic in b: it must work on any type b that derives Data. So far we have been passing in

const Nothing

which is obviously polymorphic in b since it completely ignores its argument. But how do we do something more interesting? Data and its associated combinators come from a generic programming library called Scrap Your Boilerplate (Data.Generics). A full discussion of of SYB is beyond the scope of this blog post; the SYB papers are a good starting point if you would like to know more (I would recommend reading them in chronological order, the first published paper first). For the sake of what we are trying to do it suffices to know about the existence of the following combinator:

extQ :: (Typeable a, Typeable b) => (a -> q) -> (b -> q) -> a -> q

Given a polymorphic query (forall a)—in our case this is const Nothing—extQ allows to extend the query with a type specific case (for a specific type b). We will use this to give a specific case for Expr: when we see a free variable in an expression we translate it to an application of toExpr to a Haskell variable with the same name:

metaExp :: Set VarName -> Expr -> Maybe ExpQ metaExp fvs (Var x) | x `Set.member` fvs = Just [| toExpr $(varE (mkName x)) |] metaExp _ _ = Nothing The improved quasi-quoter

With this in hand we can define our improved quasi-quoter:

prog :: QuasiQuoter prog = QuasiQuoter { quoteExp = \str -> do l <- location' c <- runIO $ parseIO (setPosition l *> topLevel parseProg) str dataToExpQ (const Nothing `extQ` metaExp (fvProg c)) c , quotePat = undefined , quoteType = undefined , quoteDec = undefined }

Note that we are extending the query for Expr, not for Prog; dataToExpQ (or, more accurately, SYB) makes sure that this extension is applied at all the right places. Running (prog2 "x" 2) now has the expected behaviour:

*Main> intIO $ intProg (prog2 "x" 2) 6 14

Indeed, when we have a variable in our code that is unbound both in Imp and in Haskell, we now get a Haskell type error:

prog2 :: VarName -> Integer -> Prog prog2 y n = [prog| var x ; x := read ; write (x + z + n) |]


TestQQAST.hs:15:19: Not in scope: ‘z’

Parenthetical remark: it is a design decision whether or not we want to allow local binding sites in a splice to “capture” meta-variables. Put another way, when we pass in "x" to prog2, do we mean the x that is bound locally in prog2, or do we mean a different x? Certainly a case can be made that we should not be able to refer to the locally bound x at all—after all, it’s not bound outside of the snippet! This is an orthogonal concern however and we will not discuss it any further in this blog post.

Quasi-quoting patterns

We can also use quasi-quoting to support patterns. This enables us to write something like

optimize :: Expr -> Expr optimize [expr| a + n - m |] | n == m = optimize a optimize other = other

As before, the occurrence of a in this pattern is free, and we intend it to correspond to a Haskell variable, not an Imp variable; the above code should correspond to

optimize (Sub (Add a n) m) | n == m = optimize a

(note that this is comparing Exprs for equality, hence the need for Expr to derive Eq). We did not mean the pattern

optimize (Sub (Add (Var "a") (Var "n")) (Var "m"))

To achieve this, we can define a quasi-quoter for Expr that supports patterns (as well as expressions):

expr :: QuasiQuoter expr = QuasiQuoter { quoteExp = \str -> do l <- location' e <- runIO $ parseIO (setPosition l *> topLevel parseExpr) str dataToExpQ (const Nothing `extQ` metaExp (fvExpr e)) e , quotePat = \str -> do l <- location' e <- runIO $ parseIO (setPosition l *> topLevel parseExpr) str dataToPatQ (const Nothing `extQ` metaPat (fvExpr e)) e , quoteType = undefined , quoteDec = undefined }

The implementation of quotePat is very similar to the definition of quoteExp. The only difference is that we use dataToPatQ instead of dataToExpQ to generate a Haskell pattern rather than a Haskell expression, and we use metaPat to give a type specific case which translates free Imp variables to Haskell pattern variables:

metaPat :: Set VarName -> Expr -> Maybe PatQ metaPat fvs (Var x) | x `Set.member` fvs = Just (varP (mkName x)) metaPat _ _ = Nothing

Note that there is no need to lift now; the Haskell variable will be bound to whatever matches in the expression.


We might be tempted to also add support for Prog patterns. While that is certainly possible, it’s of limited use if we follow the same strategy that we followed for expressions. For instance, we would not be able to write something like

opt [prog| var x ; c |] | x `Set.notMember` fvProg c = opt c

The intention here is that we can remove unused variables. Unfortunately, this will not work because this will cause a parse error: the syntax for Imp does not allow for variables for commands, and hence we also don’t allow for meta-variables at this point. This is important to remember:

By using object-level variables as stand-ins for meta-level variables, we only allow for meta-level variables where the syntax for the object-level language allows variables.

If this is too restrictive, we need to add special support in the ADT and in the corresponding parsers for meta-variables. This is a trade-off in increased expressiveness of the quasi-quotes against additional complexity in their implementation (new parsers, new ADTs).


By reusing object-level variables as stand-ins for meta variables you can reuse existing parsers and ADTs to define quasi-quoters. Using the approach described in this blog we were able to add support for quasi-quoting to a real compiler for a domain specific programming language with a minimum of effort. The implementation is very similar to what we have shown above, except that we also dealt with renaming (so that meta variables cannot be captured by binding sites in the quasi quotes) and type checking (reusing the existing renamer and type checker, of course).

Categories: Offsite Blogs

Eval forgotten unless type specified?

haskell-cafe - Sun, 10/19/2014 - 7:32am
In this passage, after the seq call, :sprint reveals x and y to be evaluated: let x = 1 + 2 :: Int let y = x + 1 :sprint x :sprint y seq y () :sprint x :sprint y The above is a verbatim quote of Marlow's Concurrency book, chapter 2. In the below, however, :sprint shows them unevaluated both before and after the seq call. let x = 1 + 2 let y = x + 1 :sprint x :sprint y seq y () :sprint x :sprint y Why? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe< at >
Categories: Offsite Discussion

looking for ongoing Haskell projects ..

haskell-cafe - Sun, 10/19/2014 - 7:31am
Hi Fellow Haskellers, I was just reading at I am looking for a list of currently active Haskell projects needing help .. :-) Vasili _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe< at >
Categories: Offsite Discussion

Am I being an idiot?

Haskell on Reddit - Sun, 10/19/2014 - 2:37am

I've been working on a data structure that sounds good in theory but it keeps eating all my heap.

data ByteGrid = ByteGrid !Int -- Width !Int -- Height !(Vector (Vector ByteArray))

It's a row, of columns, of byte arrays (8x8 chunks). In theory, I get nice caching with the 8x8 chunks. That's the idea behind it.

I'm making a real-time strategy game and the ByteGrid is being used for the pathing grid. When players place structures, I'll need to update the grid with new unpathable spots. I don't need writes to be that fast since placing structures isn't an extremely common occurrence (maybe once per frame).

The ByteGrid is "working" but it continually eats up my heap. I've tried deepseq. I've put as many strictness annotations as possible. Is there a bug in GHC? Does it not handle lots of vectors/arrays well? Am I completely missing something?


EDIT: Big thanks to tomejaguar for identifying the problem. Thanks to stere0id for a nice refactor. Here's the edited code.

submitted by Agitates
[link] [26 comments]
Categories: Incoming News

Precise timing

haskell-cafe - Sun, 10/19/2014 - 2:36am
Hi, An earlier version of this question is cross-posted at StackOverflow <> . What should I use for precise (and eventually, concurrency-compatible) timing in Haskell? I want to make rhythms. I tried the following to produce a repeating rhythm in which one note is twice as long as the other two. (That rhythm is encoded by the list [1,1,2].) import Control.Concurrent import Text.Printf import Control.Monad main = mapM_ note (cycle [1,1,2]) beat = round (10^6 / 4) -- measured in microseconds note :: Int -> IO () note n = do threadDelay $ beat * n printf "\BEL\n" When I run it the long note is three times as long as the others, not twice. If I speed it up, by changing the number 4 to a 10, the rhythm is destroyed completely: the notes all have the same length. threadDelay is guaranteed <>
Categories: Offsite Discussion