Git Product home page Git Product logo

haskell-generate's Introduction

haskell-generate

Build Status

Introduction

If you want to generate haskell source code, you could build up haskell-src-exts AST and then pretty print it. But that's easy to screw up, because haskell-src-exts doesn't include tag it's AST with a type. This library aims to fill the gap, adding type information to haskell-src-exts expressions and also managing imports for you.

Getting started

First, you need to import this library:

import Language.Haskell.Generate

This module reexports Language.Haskell.Exts.Syntax, because haskell-generate builds on top of that.

There are two main types in haskell-generate. The first is the monad Generate and the type alias ExpG. The Generate monad is used to track required imports. It also allows to generate unique names. ExpG t is just an action in the Generate monad that returns an expression of type t.

How do you build expressions? There is a number of predefined expressions for the functions in the Prelude. This allows you to just use these and combine them to new expressions. For example, let's define a expression that reads a file called "names":

readNamesFile' :: ExpG (IO String)
readNamesFile' = readFile' <>$ expr "names"

Here we use (<>$) to apply the readFile' expression to the string names. readFile' is one of the expressions already provided by haskell-generate. All expressions that are provided by haskell-generate end with an apostrophe. You can find more of them in the module Language.Haskell.Generate.PreludeDef. The expr function is used to lift the string names into an expression of type ExpG String.

Now that we have an expression, we need to bind it to a name in a module. For this job, we use another monad, the ModuleM monad. It allows you to bind expressions to names and then generate a module with those names.

Here's how we generate our module:

myModule :: ModuleG
myModule = do
  d <- addDecl (Ident "main") $ applyE2 bind' readNamesFile' putStrLn'
  return $ Just [exportFun d]

ModuleG is again a type synonym for an action in the ModuleM monad. It must either return Nothing (which omits the export list) or an export list. In this case, we export the "main" function, which we previously defined using addDecl.

The only thing left to do is to generate the actual source code for the module, for which we use the generateModule function, which takes the module name as an argument:

main :: IO ()
main = putStrLn $ generateModule myModule "Main"

If you run the program, you'll get the following output:

module Main (main) where
import qualified GHC.Base
import qualified System.IO
main
  = (GHC.Base.>>=) (System.IO.readFile ['n', 'a', 'm', 'e', 's'])
      System.IO.putStrLn

If you run this code, you'll get the contents of the "names" file. The code is a bit ugly and uses qualified imports to avoid name clashes, but it works.

Importing functions

Until now, we've only used the predefined expressions from Language.Haskell.Generate.PreludeDef, but often you'll want to use definitions from other modules that you might want to use.

You can do that using the useValue from haskell-generate. Let's look at the type of useValue:

useValue :: String -> Name -> ExpG t

useValue takes a module name in which the function is defined and the name of the function. It returns an expression of any type you which. This function is unsafe, because it cannot check that the returned type is actually the type of the function. That's why you usually given useValue an explicit type signature.

For example, suppose we want to use the function permutations from Data.List. We write the following definition for it:

permutations' :: ExpG ([a] -> [[a]]) -- Here we given an explicit type for permutations'. This is not checked, so make sure it's actually right!
permutations' = useValue "Data.List" (Ident "permutations") -- "permutations" is an identifier, not a symbol, so we use the "Ident" constructor.

Using TH to automagically import functions

If the function you want to import is already available at compile time, you can use the template haskell code from Language.Haskell.Generate.TH to generate the expression definitions. This is the approach we use for the Prelude, as an example.

Using the example from the previous section, we could also import the permutations function like this:

-- at the top of the file:
{-# LANGUAGE TemplateHaskell #-} -- Enable template haskell
import Data.List (permutations) -- The function needs to be available at compile time

declareFunction 'permutations -- This generates the same code as above, but is more type-safe because you don't have to specify the type yourself.

Contributing

If you have an idea, a question or a bug report, open an issue on github. You can also find me on freenode in the #haskell channel, my nick is bennofs.

haskell-generate's People

Contributors

bennofs avatar jpmoresmau avatar

Watchers

James Cloos avatar Vasiliy avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.