Explanation of macros; Haskell macros

Tomasz Zielonka t.zielonka at students.mimuw.edu.pl
Mon Oct 6 21:16:25 EDT 2003


mike420 at ziplip.com wrote:
> Someone pointed out that Haskell has macros. Does anyone
> know how they relate to Lisp and Scheme macros? Better, worse,
> different? 

You can find a short comparison of Scheme and Template Haskell (TH) approach
in (pages 12-13):

  http://www.haskell.org/th/papers/meta-haskell.ps

I would say that similarity between them is much bigger than between,
say, TH and C++ template meta-programming, or between Scheme and C++ tmp.

> If anyone knows them well, can you show how you would do "for i in
> ...."  using Haskell macros?

Haskell macros/templates provide mechanisms for introspection. They
allow you to process Haskell abstract syntax trees as values of
algebraic datatype Exp. That's probably a little more complicated than
in LISP and Scheme, because Haskell's syntax is much more complex. 

You could implement "for i in ..." using them, but this would be a bad
example, because as a non-strict (think "lazy") language it will handle
this code gracefully by default.

For example this code prints consecutive positive Integers starting from
1 (if run in the IO monad):

    mapM_ print [1..]

If mapM_ scares you, you can define

    for l f = mapM_ f l

and write it nicely as:
    
    for [1..] print

Besides, Haskell macros aren't applied everywhere by default as in LISP.
You have to ,,splice'' them explicitely (see the paper).

A good example of TH's power is the ability to create a statically
type-checked printf mechanism using format strings like in C. Example:

    $(printf "an int: %d, int in hex: %08x, a string: %20s") 10 255 "foo"

compiles and evaluates to

    "an int: 10, int in hex: 000000ff, a string: foo"

but

    $(printf "an int: %d, int in hex: %08x, a string: %20s") 10 "foo" 255

will raise a compile-time type error.




Recently I have used TH to generate a datatype enumeration all keywords
in some SQL dialect and to create a mapping from strings to these
datatypes. It looks like this:

  keywords :: [String]
  keywords = words
      " ADD ALL ALTER AND ANY AS ASC AUTHORIZATION BACKUP BEGIN BETWEEN \
      [... 160 keywords snipped ...]
      \ VALUES VARYING VIEW WAITFOR WHEN WHERE WHILE WITH WRITETEXT "

  kwConName :: String -> String
  kwConName = id

  -- generates abstract syntax for declaration of Keyword datatype
  dataKeyword :: Dec
  dataKeyword =
      Data
	  []                                              -- context
	  "Keyword"                                       -- datatype name
	  []                                              -- type variables
	  (map (\k -> Constr (kwConName k) []) keywords)  -- constructors
	  (words "Show Eq Ord Enum Bounded")              -- derived instances

  -- generates abstract syntax for a list of pairs like ("SELECT", SELECT)
  -- for all keywords
  keywordMapList :: ExpQ
  keywordMapList =
      foldr
	  (\k l ->
	      let str = return (Lit (String k))
		  con = return (Con (kwConName k))
	      in  [| ($str, $con) : $l |])
	  [| [] |]
	  keywords

Then in another module:

  $(return [dataKeyword])

  keywordMap :: FiniteMap String Keyword
  keywordMap = listToFM $(keywordMapList)

This way I am sure that every keyword is included in the mapping.

Best regards,
Tom

-- 
.signature: Too many levels of symbolic links




More information about the Python-list mailing list